﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace BGSpaceShooterServer
{
    internal class UDPListener
    {
        private ServerState state;
        private IPAddress ipAddress;
        private int port;
        public bool Closed { get; private set; }
        private UdpClient listener;
        private Thread listenerThread;

        public UDPListener(IPAddress ipAddress, int port)
        {
            Closed = false;
            this.ipAddress = ipAddress;
            this.port = port;
            state = ServerState.NotInitialized;
            try
            {
                listener = new UdpClient(port, AddressFamily.InterNetwork);

            }
            catch (Exception e)
            {
                System.Windows.Forms.MessageBox.Show(e.Message);
            }

            listenerThread = new Thread(new ThreadStart(Listen));

            listenerThread.Start();

            Logger.Log(this, "Started listening at " + ipAddress.ToString() + " : " + port.ToString() + "\n");
        }

        public void ChangeState(ServerState state)
        {
            if (state == ServerState.Exiting)
            {
                try
                {
                    listenerThread.Abort();
                    listener.Close();
                }
                catch (SocketException e)
                {
                    Logger.Log(this, e.Message);
                }

                Closed = true;
                Logger.Log(this, "Closed connection at " + ipAddress.ToString() + " : " + port.ToString() + "\n");
            }

            this.state = state;

            Logger.Log(this, "Listener at " + ipAddress.ToString() + " : " + port.ToString() + " changed state to " + state.ToString() + "\n");

        }

        private void Listen()
        {
            try
            {
                byte[] data;

                while (state != ServerState.Exiting)
                {
                    IPEndPoint remote = new IPEndPoint(ipAddress, port);

                    data = listener.Receive(ref remote);

                    GCHandle pinnedPacket = GCHandle.Alloc(data, GCHandleType.Pinned);
                    ClientToServerPacket packet =
                        (ClientToServerPacket)
                        Marshal.PtrToStructure(pinnedPacket.AddrOfPinnedObject(), typeof(ClientToServerPacket));
                    pinnedPacket.Free();

                    if (MessageRecieved != null && (state == ServerState.Initialization || state == ServerState.Game))
                    {
                        MessageRecieved.Invoke(this, new CTSEventArgs(packet, remote));
                    }
                }
            }
            catch (ThreadAbortException)
            {

            }
        }

        public event CTSEventHandler MessageRecieved;

    }
}
